<template>
	<view>
		<scroll-view
			class="msg-list"
			:class="speechIsOpen ? 'model-audio' : 'model-text'"
			:scroll-x="false"
			:scroll-y="true"
			:scroll-with-animation="false"
			:scroll-top="scrollTop"
		>
			<view class="list" v-if="lists && lists.length > 0">
				<block v-for="(item, index) in lists" :key="index">
					<view class="row-ai" v-if="item.user == 'AI'">
						<view class="message" :data-index="index">
							<view class="avatar">
								<image :src="aiAvatar"></image>
							</view>
							<view class="text markdown-body">
								<view v-if="speechIsOpen && lists[index - 1] && lists[index - 1].audio">
									<view class="player" @tap="switchPlay(index)">
										<view style="display: flex; align-items: center">
											<image v-if="item.playing" src="/others/static/images2/ic_player_stop.png"></image>
											<image v-else src="/others/static/images2/ic_player_play.png"></image>
											<view :class="item.playing ? 'wave' : 'nowave'">
												<view :class="'line line' + index1" v-for="(item, index1) in 6" :key="index1"></view>
												<view :class="'line line' + (5 - index1)" v-for="(item, index1) in 6" :key="index1"></view>
												<view :class="'line line' + index1" v-for="(item, index1) in 6" :key="index1"></view>
											</view>
										</view>
										<view></view>
									</view>
									<view style="clear: both"></view>
								</view>
								<towxml :content="item.message" />
								<view class="tools">
									<view>
										<view class="btn" @tap="copyText(index)">
											<image class="icon" src="/others/static/images2/ic_copy.png"></image>
											<span>复制内容</span>
										</view>
									</view>
									<view>
										<view
											v-if="speechIsOpen && (!lists[index - 1] || !lists[index - 1].audio)"
											class="btn"
											@tap="switchPlay(index)"
											style="margin-right: 20rpx"
										>
											<image v-if="item.playing" class="icon" src="/others/static/images2/ic_stop.png"></image>
											<image v-else class="icon" src="/others/static/images2/ic_speech.png"></image>
										</view>
										<view class="btn" title="重新回答" @tap="retry(index - 1)" style="margin-right: 0">
											<image class="icon" src="/others/static/images2/ic_retry.png"></image>
										</view>
									</view>
								</view>
							</view>
						</view>
					</view>

					<view class="row-user" v-else>
						<view class="message" @longpress="copyText(index)">
							<view class="avatar" style="background: #9aa37e">
								<image :src="userAvatar"></image>
							</view>
							<view class="text markdown-body">
								<view v-if="speechIsOpen && item.audio">
									<view class="player" @tap="switchPlayAudio(index)">
										<view style="display: flex; align-items: center">
											<image v-if="item.playing" src="/others/static/images2/ic_player_stop.png"></image>
											<image v-else src="/others/static/images2/ic_player_play.png"></image>
											<view :class="item.playing ? 'wave' : 'nowave'">
												<view :class="'line line' + index1" v-for="(item, index1) in 6" :key="index1"></view>
												<view :class="'line line' + (5 - index1)" v-for="(item, index1) in 6" :key="index1"></view>
												<view :class="'line line' + index1" v-for="(item, index1) in 6" :key="index1"></view>
											</view>
										</view>
										<view class="time">
											<text v-if="item.audio_length">{{ item.audio_length }}''</text>
										</view>
									</view>
									<view style="clear: both"></view>
								</view>
								<towxml :content="item.message" />
							</view>
						</view>
					</view>
				</block>
				<view class="row-ai" v-if="writing || writingText">
					<view class="message">
						<view class="avatar">
							<image :src="aiAvatar"></image>
						</view>
						<view class="text markdown-body">
							<view v-if="speechIsOpen && lists[lists.length - 1].audio">
								<view class="player" :data-index="index">
									<view style="display: flex; align-items: center">
										<image src="/others/static/images2/ic_player_stop.png"></image>
										<view class="wave">
											<view :class="'line line' + index1" v-for="(item, index1) in 6" :key="index1"></view>
											<view :class="'line line' + (5 - index1)" v-for="(item, index1) in 6" :key="index1"></view>
											<view :class="'line line' + index1" v-for="(item, index1) in 6" :key="index1"></view>
										</view>
									</view>
									<view></view>
								</view>
								<view style="clear: both"></view>
							</view>
							<towxml :content="writingText" :writing="!!(writing || writingText)" />
							<view class="tools">
								<view>
									<view class="btn" @tap="stopFetch">
										<image class="icon" src="/others/static/images2/ic_stop.png"></image>
										<span>停止回复</span>
									</view>
								</view>
							</view>
						</view>
					</view>
				</view>
			</view>
			<welcome v-else module="chat" :ai-avatar="aiAvatar" :title="welcome.title" :desc="welcome.desc" :tips="welcome.tips" @setMessage="setMessage"></welcome>
		</scroll-view>
	</view>
</template>

<script>
import { baseURL,version } from '@/config/app';
import { getHistoryMsg } from '@/api/others.js';
import welcome from '@/others/components/welcome2/index';
import towxml from '@/others/towxml/towxml';
const app = getApp();
export default {
	components: {
		welcome,
		towxml
	},
	data() {
		return {
			scrollTop: 0, //滚动条位置
			group_id: 0,
			lists: null,
			message: '',
			writingText: '',
			writing: false,
			_audioTemp: '',
			textStacks: [],
			textOutputSi: 0,
			_requestTask: null
		};
	},
	props: {
		aiName: {
			type: String,
			default: ''
		},
		aiIndex: {
			type: Number,
			default: 0
		},
		activeAiIndex: {
			type: Number,
			default: 0
		},
		welcome: {
			type: Object,
			default: {}
		},
		userAvatar: {
			type: String,
			default: '/others/static/images2/ic_user_logo.png'
		},
		aiAvatar: {
			type: String,
			default: '/others/static/images2/ic_ai_logo.png'
		},
		speechIsOpen: {
			type: Boolean,
			default: true
		}
	},

	mounted() {
		activeAiIndex: {
			const aiIndex = this.aiIndex;
			const activeAiIndex = this.activeAiIndex;
			const lists = this.lists;
			if (aiIndex >= activeAiIndex - 1 && aiIndex <= activeAiIndex + 1 && lists === null) {
				this.getHistoryMsg();
			}
		}
	},
	methods: {
		sendText(message, audio = '', audioLength = 0) {
			// console.log(message, '问题');
			let that = this;
			if (this.writing) {
				return;
			}
			message = this.trim(message);
			if (!message) {
				uni.showToast({
					title: '请输入你的问题',
					icon: 'none',
					duration: 2000
				});
				return;
			}
			if (this.textOutputSi) {
				clearInterval(this.textOutputSi);
				this.textOutputSi = 0;
				this.textStacks = [];
			}

			let lists = this.lists;
			lists.push({
				user: '我',
				message: message,
				audio: audio,
				audio_length: Math.ceil(audioLength / 1000)
			});
			this.setMessage('');
			this.lists = lists;
			this.writing = true;
			this.scrollToBottom();
			// stream
			this.textOutputSi = setInterval(() => {
				// console.log(this.textStacks, '000');
				if (this.textStacks.length > 0) {
					// console.log(this.textStacks, '111');
					const char = this.textStacks.shift();
					this.writingText = this.writingText + char;
					if (audio) {
						this.audioTemp = this.audioTemp + char;
						if (char == '\n') {
							uni.$emit('addToPlay', {
								detail: {
									text: this._audioTemp,
									aiIndex: this.aiIndex,
									msgIndex: this.lists.length
								}
							});
							this._audioTemp = '';
						} else {
							this._audioTemp = this._audioTemp + char;
						}
					}
					this.scrollToBottom();
				} else if (!this.writing) {
					// console.log(this.textStacks, '2222');
					clearInterval(this.textOutputSi);
					this.textOutputSi = 0;
					if (audio) {
						uni.$emit('addToPlay', {
							detail: {
								text: this._audioTemp,
								aiIndex: this.aiIndex,
								msgIndex: this.lists.length
							}
						});
						uni.$emit('addToPlay', {
							detail: {
								text: '[end]',
								aiIndex: this.aiIndex,
								msgIndex: this.lists.length
							}
						});
						this._audioTemp = '';
					}
					if (this.writingText) {
						lists.push({
							user: 'AI',
							message: this.writingText,
							playing: audio ? true : false
						});
						this.lists = lists;
					}
					this.writingText = '';
					this.scrollToBottom();
				}
			}, 30);
			// 网络请求
			const _this = this;
			const requestTask = uni.request({
				url: baseURL + '/shopapi/gpt/sendText',
				timeout: 300000,
				responseType: 'text',
				method: 'POST',
				enableChunked: true,
				//配置这里
				header: {
					'Content-Type': 'application/json',
					// 'X-Site': uni.getStorageSync('sitecode'),
					token: this.$store.getters.token,
					version: version
				},
				data: {
					ai: _this.aiName,
					message: message,
					audio: audio,
					audio_length: audioLength
				},
				success: (res) => {
					_this.writing = false;
				},
				fail: (error) => {
					_this.writing = false;
				}
			});
			// 这里监听消息
			requestTask.onChunkReceived(function (res) {
				// ArrayBuffer to Text
				const uint8Array = new Uint8Array(res.data);
				let char = String.fromCharCode.apply(null, uint8Array);
				char = decodeURIComponent(escape(char));
				if (char.indexOf('[error]') !== -1) {
					lists.pop();
					this.writing = false;
					this.writingText = '';
					this.lists = lists;
					_this.setMessage(message);
					clearInterval(_this.textOutputSi);
					_this.textOutputSi = 0;
					const error = char.replace('[error]', '');
					console.log(error, 'error');
					uni.showToast({
						title: error,
						icon: 'none',
						duration: 2000
					});
					return;
				}
				_this.writing = true;
				let textStacks = _this.textStacks;
				for (var i = 0; i < char.length; i++) {
					textStacks.push(char[i]);
					_this.textStacks = textStacks;
				}
			});
			_this._requestTask = requestTask;
		},
		stopFetch() {
			this._requestTask.abort();
			this.writing = false;
			this.textStacks = [];
			this._audioTemp = '';

			let lists = this.lists;
			const writingText = this.writingText;
			uni.$emit('stopPlay');
			setTimeout(() => {
				if (!writingText) {
					lists.pop();
					this.lists = lists;
				}
			}, 60);
		},
		retry(index) {
			const _this = this;
			uni.showModal({
				title: '提示',
				content: '确定重新回答吗？',
				success: function (res) {
					if (res.confirm) {
						const item = _this.lists[index];
						const message = item.message;
						const audio = item.audio;
						const audioLength = parseInt(item.audio_length);
						_this.sendText(message, audio ? audio : '', audioLength ? audioLength * 1000 : 0);
					}
				}
			});
		},

		switchPlay(msgIndex) {
			let lists = this.lists;
			let item = lists[msgIndex];

			if (item.playing) {
				uni.$emit('stopPlay');
			} else {
				uni.$emit('startPlay', {
					detail: {
						msgIndex: msgIndex,
						aiIndex: this.aiIndex
					}
				});
			}
		},
		setPlayStart(index) {
			if (!this.lists[index]) {
				return;
			}
			this.lists[index].playing = true;
			this.$forceUpdate();
			// console.log('当前状态')
		},

		setPlayStop(index) {
			if (!this.lists[index]) {
				return;
			}
			this.lists[index].playing = false;
			this.$forceUpdate();
		},
		switchPlayAudio(msgIndex) {
			let item = this.lists[msgIndex];
			if (this.writing) {
				uni.showToast({
					title: 'AI输出中，请稍候',
					icon: 'none',
					duration: 2000
				});
				return;
			}
			if (!item.playing) {
				if (item.audio) {
					uni.$emit('playAudio', {
						detail: {
							msgIndex: msgIndex,
							aiIndex: this.aiIndex,
							audio: item.audio
						}
					});
				}
			} else {
				uni.$emit('stopPlay');
			}
		},
		getHistoryMsg() {
			getHistoryMsg({
				ai: this.aiName,
				page: 1,
				pagesize: 10
			}).then((res) => {
				this.lists = res;
				setTimeout(() => {
					this.scrollToBottom();
				}, 300);
			});
		},
		scrollToBottom() {
			console.log('到底部');
			setTimeout(() => {
				let query = uni.createSelectorQuery().in(this);
				query.select('.list').boundingClientRect((res) => {
					if (res) {
						this.scrollTop = parseInt(res.height);
					}
				});
				query.exec((res) => {});
			}, 200);
		},
		copyText(index) {
			const text = this.trim(this.lists[index].message);
			uni.setClipboardData({
				data: text
			});
		},
		trim(str) {
			if (str) {
				str = str.replace(/(^\s*)|(\s*$)/g, '');
			}
			return str;
		},
		setMessage(e) {
			const message = e.detail ? e.detail : e;
			uni.$emit('setMessage', {
				detail: message
			});
		},
		isGpt4(name) {
			return ['openai4', 'azure_openai4', 'wenxin4', 'claude2', 'hunyuan4'].includes(name);
		}
	}
};
</script>
<style lang="scss">
.msg-list {
	width: 100%;
	height: calc(100vh - 132rpx);
	top: 0;
	bottom: 132rpx;
	left: 0;
	box-sizing: border-box;
	overflow: hidden;
}

.message {
	display: flex;
	justify-content: flex-start;
	padding: 40rpx 40rpx;
	border-top: 2rpx solid #eee;
}

.message:last-child {
	border-bottom: 0;
}

.message .text {
	width: 596rpx;
	color: #343541;
	line-height: 52rpx;
	font-size: 32rpx;
	word-break: break-all;
	padding: 4rpx 0;
	overflow: hidden;
}

.message .text span {
	display: inline;
}

.message .avatar {
	width: 48rpx;
	height: 48rpx;
	background: #04babe;
	margin-right: 30rpx;
	color: #fff;
	font-size: 28rpx;
	border-radius: 8rpx;
	display: flex;
	align-items: center;
	justify-content: center;
	margin-top: 6rpx;
	overflow: hidden;
}

.message .avatar image,
.message .avatar img {
	width: 48rpx;
	height: 48rpx;
}

.model-text .row-user {
	background: #fff;
}

.model-audio .message {
	padding: 30rpx 20rpx;
	border: none;
}
.model-audio .message .text {
	max-width: 600rpx;
	width: auto;
	border-radius: 10px;
	display: block;
	padding: 30rpx 40rpx;
	box-sizing: border-box;
}
.model-audio .row-user .message {
	display: block;
	width: 100%;
	float: right;
	padding: 20rpx 8rpx 20rpx 0;
	position: relative;
}
.model-audio .row-user .message .avatar {
	position: absolute;
	right: 20rpx;
	top: 24rpx;
	margin-right: 0;
}
.model-audio .row-user .message .text {
	float: right;
	background: #d0e4e2;
	color: #fff;
	margin-right: 80rpx;
}
.model-audio .row-user .message .text .h2w-light {
	color: #fff !important;
}

.model-audio .row-ai .message .text {
	float: right;
	background: #fff;
	color: #666;
}

.tools {
	display: flex;
	align-items: center;
	justify-content: space-between;
	margin-top: 12rpx;
}

.tools .btn {
	font-size: 32rpx;
	color: #04babe;
	display: flex;
	align-items: center;
	float: left;
	margin-right: 10rpx;
}
.tools .btn:active {
	opacity: 0.6;
}
.tools .btn .icon {
	width: 32rpx;
	height: 32rpx;
	margin-right: 6rpx;
}

/**声音动画**/
.player {
	width: 360rpx;
	height: 88rpx;
	/* background: #04BABE; */
	display: flex;
	align-items: center;
	justify-content: space-between;
	box-sizing: border-box;
	padding: 0 24rpx;
	border-radius: 10rpx;
	margin-bottom: 20rpx;
}
.player:active {
	opacity: 0.6;
}
.player image {
	width: 48rpx;
	height: 48rpx;
	margin-right: 20rpx;
}
.player .wave,
.player .nowave {
	width: 188rpx;
	height: 36rpx;
	text-align: center;
	position: relative;
	display: flex;
	justify-content: space-between;
	align-items: center;
}
.player .wave .line,
.player .nowave .line {
	width: 4rpx;
	height: 24rpx;
	background: #04babe;
	margin: 0 4rpx;
	border-radius: 4rpx;
	display: inline-block;
}
@keyframes jump {
	0% {
		transform: scale(1, 1);
	}
	100% {
		transform: scale(1, 0.2);
	}
}
.player .wave .line0 {
	animation: jump 0.5s 1.6s linear infinite alternate;
}
.player .wave .line1 {
	animation: jump 0.5s 1.4s linear infinite alternate;
}
.player .wave .line2 {
	animation: jump 0.5s 1.2s linear infinite alternate;
}
.player .wave .line3 {
	animation: jump 0.5s 1s linear infinite alternate;
}
.player .wave .line4 {
	animation: jump 0.5s 0.8s linear infinite alternate;
}
.player .wave .line5 {
	animation: jump 0.5s 0.6s linear infinite alternate;
}
.player .wave .line6 {
	animation: jump 0.4s linear infinite alternate;
}
.player .time {
	font-size: 28rpx;
	color: #04babe;
}
.row-ai .player {
	float: left;
	background: #f0f2f4;
	/* background: rgba(0,0,0,0.05) */
}

.row-user .player {
	float: right;
	background: #ddefed;
	/* background: rgba(255,255,255,0.3) */
}
</style>
